#if defined(_MSC_VER)
#pragma once
#endif

#ifndef LM_CORE_FRAME_H
#define LM_CORE_FRAME_H

#include "geometry.h"

class Frame {
public:
	// Make sure vectors are normalized!!
	inline Frame() {}
	inline Frame(const Frame &other) {
		s = other.s;
		t = other.t;
		n = other.n;
	}
	Frame &operator=(const Frame &other) {
		s = other.s;
		t = other.t;
		n = other.n;
		return *this;
	}
	bool operator==(const Frame &other) {
		return s == other.s && t == other.t && n == other.n;
	}
	bool operator!=(const Frame &other) {
		return s != other.s || t != other.t || n != other.n;
	}
	inline Frame(const Vector3 &vs, const Vector3 &vt, const Normal &vn) :
	s(vs), t(vt), n(vn) {}
	inline Frame(const Vector3 &x, const Vector3 &y, const Vector3 &z) :
	s(x), t(y), n(Normal(z)) {}
	
	inline Frame(const Vector3& vn) : n(Normal(vn)) {
		CoordinateSystem(n, &s, &t);
	}
	inline Frame(const Normal &vn) : n(vn) {
		CoordinateSystem(n, &s, &t);
	}
	inline Vector3 ToLocal(const Vector3 &v) const {
		return Vector3(Dot(v, s), Dot(v, t), Dot(v, n));
	}
	inline Vector3 ToWorld(const Vector3 &v) const {
		return s * v.x + t * v.y + Vector3(n) * v.z;
	}
	// Make sure v is normalized!!
	inline static float CosTheta(const Vector3 &v) {
		return v.z;
	}
	inline static float AbsCosTheta(const Vector3 &v) {
		return fabsf(v.z);
	}
	inline static float CosTheta2(const Vector3 &v) {
		return v.z * v.z;
	}
	inline static float CosPhi(const Vector3 &v) {
		float sintheta = SinTheta(v);
		if(sintheta == 0.f) return 1.f;
		return Clamp(v.x / sintheta, -1.f, 1.f);
	}
	inline static float SinTheta(const Vector3 &v) {
		float temp = max(0.f, 1.0f - v.z * v.z);
		return std::sqrt(temp);
	}
	inline static float SinPhi(const Vector3 &v) {
		float sintheta = SinTheta(v);
		if (sintheta == 0.f) return 0.f;
		return Clamp(v.y / sintheta, -1.f, 1.f);
	}
	inline static float SinTheta2(const Vector3 &v) {
		return max(0.f, 1.0f - v.z * v.z);
	}
	inline static float TanTheta(const Vector3 &v) {
		float temp = max(0.f, 1.0f - v.z * v.z);
		return std::sqrt(temp) / v.z;
	}
	inline static bool SameHemisphere(const Vector3 &w, const Vector3 &wp) {
		return w.z * wp.z > 0.f;
	}
	inline static Vector3 OtherHemisphere(const Vector3 &v) {
		return Vector3(v.x, v.y, -v.z);
	}
	inline std::string ToString() const {
		std::ostringstream oss;
		oss << "Frame[" << std::endl
			<< "  s = " << s.ToString() << "," << std::endl
			<< "  t = " << t.ToString() << "," << std::endl
			<< "  n = " << n.ToString() << std::endl
			<< "]";
		return oss.str();
	}
	// Public Data of Frame
	Vector3 s, t;
	Normal n;
};


#endif	// LM_CORE_FRAME_H
